﻿#include "MessageQueue.h"

constexpr auto RUMTIME_ERR_QUEUE_EXITED = "Queue is stopped and empty!";

void MessageQueue::enqueue(const Message& msg)
{
	std::unique_lock<std::mutex> lock(mtx);
	queue.push(msg);
	condition.notify_one();
}

Message MessageQueue::dequeue()
{
	std::unique_lock<std::mutex> lock(mtx);
	while (!stop && (queue.empty() || !queue.top().shouldExecute()))
	{
		if (queue.empty())
		{
			condition.wait(lock);
		}
		else
		{
			auto waitTime = queue.top().getExecuteAt();
			std::chrono::steady_clock::duration now = std::chrono::steady_clock::now().time_since_epoch();
			auto gap = std::chrono::duration_cast<std::chrono::milliseconds>((waitTime - now).time_since_epoch()).count();
			printf("Wait time[%lld]\n", gap);
			condition.wait_until(lock, waitTime, [this, &waitTime]
				{
					return queue.empty() || stop || queue.top().getExecuteAt() < waitTime;
				});
		}
	}
	if (stop && queue.empty())
	{
		throw std::runtime_error(RUMTIME_ERR_QUEUE_EXITED);
	}
	Message message = queue.top();
	queue.pop();
	return message;
}

bool MessageQueue::empty() const
{
	std::unique_lock<std::mutex> lock(mtx);
	return queue.empty();
}

void MessageQueue::quit()
{
	{
		std::unique_lock<std::mutex> lock(mtx);
		stop = true;
		printf("Message queue quitted!\n");
	}
	condition.notify_one();
}

void MessageQueue::clear()
{
	{
		std::unique_lock<std::mutex> lock(mtx);
		while (!queue.empty())
		{
			queue.pop();
		}
		printf("Message queue cleared!\n");
	}
	condition.notify_one();
}

Looper::Looper()
{
	worker = std::thread(&Looper::loop, this);
}

Looper::~Looper()
{

	isRunning = false;
	queue.clear();
	queue.quit();
	if (worker.joinable())
	{
		worker.join();
	}
	printf("Looper deleted!\n");
}

void Looper::enquee(int what, long delay, std::function<void(const Message&)> task)
{
	queue.enqueue(Message(task, what, false, delay));
}

void Looper::enqueePriority(int what, long delay, std::function<void(const Message&)> task)
{
	queue.enqueue(Message(task, what, true, delay));
}

void Looper::loop()
{
	while (true)
	{
		if (!isRunning)
		{
			break;
		}
		try 
		{
			Message message = queue.dequeue();
			if (message.shouldExecute())
			{
				message.task(message);
			}
			else
			{
				queue.enqueue(message);
			}
		}
		catch (const std::runtime_error& e)
		{
			printf("Message queue exited.\n");
			if (e.what() == RUMTIME_ERR_QUEUE_EXITED)
			break;
		}
	}
	printf("Looper exited...\n");
}

void Looper::removeAllMessages()
{
	queue.clear();
}

void Looper::quit()
{
	isRunning = false;
	queue.quit();
}
